#!/bin/bash

# 加载此文件, 会自动加载所有的函数. 同时用这个变量标记, 避免重复加载
export BASE_FUNC_DEF=1

# 检查当前系统类型.
# 返回:  mac, linux, windows
#
system_type() {
  if [[ "$(uname)" == "Darwin" ]]; then
    # Mac OS X 操作系统
    echo 'mac'
  elif [[ "$(expr substr "$(uname -s)" 1 5)" == "Linux" ]]; then
    # GNU/Linux操作系统
    echo "linux"
  elif [[ "$(expr substr "$(uname -s)" 1 10)" == "MINGW32_NT" ]]; then
    # Windows NT操作系统
    echo 'windows'
  fi
}

# 生成随机字符串
#
_uuid() {
  uuidgen | sed 's/-//g' | tr '[:upper:]' '[:lower:]'
}

# 随机数生成函数.
# 参数:
# $1: min 最小值(包含)
# $2: max 最大值(包含)
# 示例: random 1 100
# 返回: 1~100之间的随机数
_rand() {
  local min max num
  min=$1
  max=$(($2 - $min + 1))
  num=$(($RANDOM + 1000000000)) #增加一个10位的数再求余
  echo $(($num % $max + $min))
}

#  ---------------------------------------------- 日期时间工具类  --------------------------------------------------------

# 输出当前时间戳,10位 , 不包含ms
timestamp() {
  date +%s
}

# 输出当前时间戳,13位 , 包含ms
# 注意: 由于 Mac OS X 操作系统不支持毫秒级时间戳, 所以这里直接补000
# 调用示例:
# ms=`timestamp_ms`
timestamp_ms() {
  if [[ "$(system_type)" == linux ]]; then
    date +%s%3N
    return
  fi
  # Mac OS X 操作系统, 只能精确到秒. 直接补000
  if [[ "$(system_type)" == mac ]]; then
    date +%s000
    return
  fi
  echo "不支持的操作系统类型: $(system_type)"
  exit 1
}

# 当前时间戳转换为日期
timestamp_date() {
  date +"%Y-%m-%d %H:%M:%S"
}

# 当前时间: %Y-%m-%d %H:%M:%S
now() {
  timestamp_date
}

# 时间戳格式化.
# 参数:
# $1: timestamp 时间戳 (秒,如果是毫秒需要截取前10位)
# $2: fmt 格式化字符串, 例如: %Y-%m-%d %H:%M:%S , %Y%m%d%H%M%S ,
#         具体格式定义可以参考: https://www.ltsplus.com/linux/linux-date-format-shell-script
# 重要格式说明:
# %s:    时间戳 (10位)
# %Y:    年份 示例: 2013
# %m:    月份 示例: 07
# %d:    day of month (e.g, 01) 示例: 01
# %H:    hour(00..23) 示例: 23
# %M:    minute (00..59) 示例: 59
# %S:    秒
# %N     nanoseconds (000000000..999999999)
# 输出: 格式化后的日期
# 示例: timestamp_format 1666766635 "%Y-%m-%d %H:%M:%S"
timestamp_format() {

  local timestamp="$1"
  local fmt="$2"
  local formatted_date

  # 1666766635000
  if [ "${#timestamp}" -gt 10 ]; then
    timestamp=${timestamp:0:10}
  fi

  case "$(system_type)" in
  'linux')
    formatted_date=$(date -d "@${timestamp}" +\""${fmt}"\")
    ;;
  'mac')
    #
    # Mac OS X 操作系统, 另外一种实现.
    # date -jf "%s" "${timestamp}" +"%Y-%m-%d %H:%M:%S"
    # -j: 不设置系统时间
    # -f: 使用指定格式解析时间
    # %s: 时间戳
    # %Y: 年份
    # %m: 月份
    # %d: 日期
    # %H: 小时
    # %M: 分钟
    # %S: 秒

    # 参数说明:
    # -r: 显示指定时间戳
    formatted_date=$(date -r "${timestamp}" +"${fmt}")
    ;;
  *)
    echo "不支持的操作系统类型: $(system_type)"
    exit 1
    ;;
  esac

  echo "$formatted_date"
}

# 时间戳转日期: %Y%m%d%H%M%S
timestamp_to_yyyyMMddHHmmss() {
  timestamp_format "$1" "%Y%m%d%H%M%S"
}

# 时间戳转日期: %Y-%m-%d %H:%M:%S
# $1: 时间戳 (秒,如果是毫秒需要截取前10位)
# 示例: timestamp_to_date 1666766635
# 输出: 2022-10-26 14:43:55
#
timestamp_to_date() {
  timestamp_format "$1" "%Y-%m-%d %H:%M:%S"
}


# 功能: 把 yyyy-MM-dd HH:mm:ss 转换为时间戳
# 示例: timestamp_from_date '2024-01-01 00:00:00'
timestamp_from_date() {
  local date="$1"
  local timestamp
  case "$(system_type)" in
  'linux')
    timestamp=$(date -d "${date}" +%s)
    ;;
  'mac')
    timestamp=$(date -j -f "%Y-%m-%d %H:%M:%S" "${date}" +%s)
    ;;
  *)
    echo "不支持的操作系统类型: $(system_type)"
    exit 1
    ;;
  esac
  echo "$timestamp"
}

#  ---------------------------------------------- 字符串工具类  --------------------------------------------------------

# trim input string $1
# 功能说明
# 参数说明
# $1: 待处理的字符串
# 返回值: trim后的字符串
## ============================================================================
trim() {
  echo "$1" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//'
}

# 函数:trim
# shellcheck disable=SC2001
func_trim() {
  echo "$1" | sed 's;^[ ]*\(.*[^ ]\)[ ]*$;\1;'
}

# 工具函数: 把字符串中的以空格分割的字符串重新分割为多行. 例如: "a b c" => "a\nb\nc"
# 参数列表:
# $1: inputString 输入字符串
# $2: splitter 分割符, 默认为空格
strings_line_to_multiline() {
  local splitter=' '
  if [ -n "$2" ]; then
    splitter="$2"
  fi
  local multi_lines="${1//${splitter}/$'\n'}"
  echo "${multi_lines}"
}

# 工具函数: 把字符串中的以多行分割的字符串重新分割为一行. 例如: "a\nb\nc" => "a b c"
# 参数列表:
# $1: inputString 输入字符串
# 输出: 一行字符串
strings_multiline_to_line() {
  local single_line="${1//$'\n'/ }"
  echo "${single_line}"
}

# 获取文件名后缀(包含点号), 并且转换为小写, 如果没有后缀,则返回空字符串
# $1: file_name
# 示例: func_get_file_name_suffix "test.txt"
# 输出: .txt
func_get_file_name_suffix() {
  local file_name="$1"
  # if file_name do not contain '.' ; return empty string
  if [[ "${file_name}" =~ .*\.[0-9A-Za-z]+ ]]; then
    # 从$string开头删除最长匹配$substr子串	${string##substr} , 并且转换为小写
    echo ".${file_name##*.}" | tr '[:upper:]' '[:lower:]'
    return
  fi
  echo ""
}

# 获取文件名后缀(不含点号), 并且转换为小写, 如果没有后缀,则返回空字符串
# $1: file_name
# 示例: func_get_file_name_suffix "test.txt"
# 输出: txt
func_get_file_name_suffix_without_dot() {
  local file_name="$1"
  # if file_name do not contain '.' ; return empty string
  if [[ "${file_name}" =~ .*\.[0-9A-Za-z]+ ]]; then
    # 从$string开头删除最长匹配$substr子串	${string##substr} , 并且转换为小写
    echo "${file_name##*.}" | tr '[:upper:]' '[:lower:]'
    return
  fi
  echo ""
}

#  ------------------------------------------------ JSON 工具类  --------------------------------------------------------

# 判断是否是有效的json字符串
func_is_valid_json() {
  if jq -e . >/dev/null 2>&1 <<<"$1"; then
    return 0
  else
    return 1
  fi
}

# 读取jsonParam的key对应的值. 如果key不存在,则返回空null (不是空字符串)
# $1 json
# $2 key
# 示例: JSON_PARAM_STRING '{"key":"value"}' '.key'
# 返回: value
JSON_PARAM_STRING() {
  local key="$2"
  # 对key进行处理,如果不是以.开头,则加上.
  if [[ ! "$key" =~ \..* ]]; then
    key=".$key"
  fi
  echo "$1" | jq -r "$key"
}

# 读取jsonParam的key对应的值. 如果key不存在,则返回空String
# $1 json
# $2 key
JSON_PARAM_STRING_TRIM2EMPTY() {

  local value
  local key="$2"
  # 对key进行处理,如果不是以.开头,则加上.
  if [[ ! "$key" =~ \..* ]]; then
    key=".$key"
  fi

  value=$(echo "$1" | jq -r "$key")
  if [[ ${value} == null ]]; then
    echo ""
    exit 0
  fi
  echo "$value"
}

# 读取jsonParam中的key对应的值. 如果key不存在,则返回空 null
# 注意:
# 1) 如果key的值为string,实际会返回: "StringValue"
# 2) 如果是对象,返回的是json字符串
JSON_PARAM_OBJ() {
  local key="$2"
  # 对key进行处理,如果不是以.开头,则加上.
  if [[ ! "$key" =~ \..* ]]; then
    key=".$key"
  fi
  echo "$1" | jq "$key"
}

# JSON数据构建器. 可以像构建MAP一样构建JSON对象
# 示例:
# JSON_BUILDER key1 val1 key2 '{"key":"value"}'
#
# https://unix.stackexchange.com/questions/686785/unix-shell-quoting-issues-error-in-jq-command
# https://stackoverflow.com/questions/70617932/bash-script-to-add-a-new-key-value-pair-dynamically-in-json
JSON_BUILDER() {

  local json='{}'
  for ((cnt = 0; cnt < $#; cnt = cnt + 2)); do
    #echo "key=${*:cnt+1:1}, value=${*:cnt+2:1}"
    local key="${*:cnt+1:1}"
    local val="${*:cnt+2:1}"

    # if [[ "${val}" =~ ^[0-9]+(.[0-9]+){0,1}$ ]] || [[ "$val" == "true" ]] || [[ "$val" =~ ^\{.*\}$ ]]; then
    # 去除数字. 只把对象和bool值添加到json中
    if  [[ "$val" == "true" ]] || [[ "$val" =~ ^\{.*\}$ ]]; then
      # 场景: bool值 , 数字 , 对象 时, 直接添加
      json=$(echo "$json" | jq '. +=  { '"\"${key}\""' : '"${val}"'}')
    else
      # value是一个string的情况下,要双引号
      json=$(echo "$json" | jq '. +=  { '"\"${key}\""' : '"\"${val}\""'}')
    fi
  done
  #cat <<< $(jq '.student1 += { "Phone'"${m}"'" : '"${i}"'}' Students.json) > Students.json
  echo "${json}"
}

# REST_API 返回数据格式定义
# 格式示例:
# {
#     "ret": true
# }
# 参数:
# $1 - ret (true|false) 必须
# $2 - errcode          可选
# $3 - errmsg           可选
# $4 - data - jsonObject json对象 可选
# 一个参数: API_RESPONSE_RETURN true
# 两个参数: API_RESPONSE_RETURN true  jsonData
# 两个参数: API_RESPONSE_RETURN false msg
# 两个参数: API_RESPONSE_RETURN false code
API_RESPONSE_RETURN() {
  local ret=true
  local errcode=0
  local errmsg=""
  local data=null

  case $# in
  1)
    ret=${1}
    ;;
  2)
    ret="$1"
    # echo "================"
    # echo "$2" | od -c
    # echo "================"

    # 两个参数:
    # 第二个参数可能是 code / msg / data
    # 左边的参数 $2 必须加双引号
    if [[ "$2" =~ ^[-]{0,1}[0-9]+$ ]]; then
      # errCode
      errcode=$2
    elif [[ "$2" =~ ^\{.*\}$ ]]; then
      # data
      data="$2"
    else
      errmsg="$2"
    fi
    ;;
  ?)
    ret=${1}
    errcode="$2"
    errmsg="$3"
    if [[ "$4" =~ \{.*\} ]]; then
      data="$4"
    fi
    ;;
  esac

  if [ "$ret" = false ] && [ "$errcode" = 0 ]; then
    errcode=-1
  fi

  # shellcheck disable=SC2155
  local REST_API_JSON=$(jq -n \
    --arg ret "${ret}" \
    --argjson errcode "$errcode" \
    --arg errmsg "$errmsg" \
    --argjson data "$data" \
    '
      {
          ret: $ret | test("true"),
          errcode: $errcode ,
          errmsg: $errmsg,
          data: $data
      }
      ')
  echo "$REST_API_JSON"
}

# REST_API 调用接口, 并直接打印返回结果. 这个不适合在脚本中调用, 适合在命令行中调用 ( 未调试完成 )
# 参数:
# $1 - api_url
# $2 - api_method (GET/POST/PUT/DELETE)
# $3 - api_data
# $4 - api_header
# $5 - api_timeout
REST_API_CALL_AND_PRINT() {

  local api_url="$1"
  local api_method="${2:-GET}"
  local api_data="$3"
  local api_header="${4:-contentType: application/json}"
  local api_timeout="${5:-60}"
  lacal AUTH_HEADER="${6:-leo-fct-auth: 1691571163}"

  local api_response
  api_response=$(curl -s -X "${api_method}" -H "${api_header}" -d "${api_data}" --connect-timeout "${api_timeout}" "${api_url}" 2>/dev/null)

  status=$(echo "${api_response}" | jq .status)

  # 返回成功时, 只输出data字段
  if [[ $status == 200 ]]; then
    echo "${api_response}" | jq .data
  else
    echo "${api_response}" | jq .
  fi
}

#  ------------------------------------------------ debug log 工具类  ---------------------------------------------------

function git_location_info() {
  local repoName="$(git config --get remote.origin.url | sed 's/.*\///g')"
  local branch="$(git branch 2>/dev/null | grep '\*')"
  echo "[${repoName}:${branch:-N/A}]"
}

#功能说明: 通用日志 输出函数
#参数说明:
#    $1:level (error/warn/info/concern)
#    $2:message
#使用方法:
#    func_log_print "level" "message"
function func_log_print() {
  local level="$1"
  local msg="$2"

  case $level in
  error) printf "\033[41;30;1m${msg}\033[0m\n" ;;
  warn) printf "\033[43;30;1m${msg}\033[0m\n" ;;
  info) printf "\033[47;30;1m${msg}\033[0m\n" ;;
  concern) printf "\033[42;30;1m${msg}\033[0m\n" ;;
  *) echo "${msg}" ;;
  esac
}

#功能说明: 返回当前函数信息输出格式
#参数说明: 无参数
#使用方法:
#    func_info_pattern
##############################################################################
function func_info_pattern() {
  local info_pattern=""
  local depth=${#FUNCNAME[*]}
  for ((i = 3; i < depth; i++)); do
    info_pattern="$info_pattern@@${FUNCNAME[i]}:${BASH_LINENO[i - 1]}"
  done
  # get file name
  #    local file_depth=${#BASH_SOURCE[*]}
  #    for (( i = 0 ; i < file_depth  ; i++ ))
  #    do
  #      echo "file[$i]: [${BASH_SOURCE[i]}],    func:${FUNCNAME[i]} , BASH_LINENO:${BASH_LINENO[i]}" >&2
  #  done
  #
  #    echo "file_len: ${#BASH_SOURCE[*]},func_len: ${#FUNCNAME[*]}" >&2

  local FILE_NAME="${BASH_SOURCE[2]##*/}"
  # 日志格式: [函数名(文件名:行号)] [git仓库名:分支名] [日志内容]
  echo "${FUNCNAME[2]}(${FILE_NAME}:${BASH_LINENO[1]})$info_pattern"
}

# 功能说明: INFO 日志输出函数
echo_info() {
  local info_pattern=$(func_info_pattern)
  #func_log_print "info" "[+] $(date +'%Y-%m-%d %H:%M:%S') [INFO   ][$info_pattern] $(git_location_info): $*" 1>&2
  printf "\033[32;40;1m[+] $(date +'%Y-%m-%d %H:%M:%S') [INFO   ][$info_pattern] $(git_location_info)\033[0m: $*\n" 1>&2
}

echo_error() {
  local info_pattern=$(func_info_pattern)
  func_log_print "error" "[+] $(date +'%Y-%m-%d %H:%M:%S') [ERROR  ][$info_pattern] $(git_location_info): $*" 1>&2
}

# 对于debug函数的输出.只有程序运行在debug级别的时候.才输出
echo_debug() {
  # "\033[43;30;1m${msg}\033[0m"
  if [[ -z "${debug}" ]]; then
    return
  fi

  # 日志格式: [日期时间] [日志级别] [函数名(文件名:行号)] [git仓库名:分支名] [日志内容]
  local info_pattern=$(func_info_pattern)
  #  printf "\033[32;40;1m[+] $(date +'%Y-%m-%d %H:%M:%S') [DEBUG  ][$info_pattern] $(git_location_info)\033[0m: $*\n" 1>&2
  printf "\033[47;30;1m[+] $(date +'%Y-%m-%d %H:%M:%S') [DEBUG  ][$info_pattern] $(git_location_info)\033[0m: $*\n" 1>&2
}

echo_concern() {
  local info_pattern=$(func_info_pattern)
  func_log_print "concern" "[+] $(date +'%Y-%m-%d %H:%M:%S') [CONCERN][$info_pattern] $(git_location_info): $*" 1>&2
}

echo_warn() {
  local info_pattern=$(func_info_pattern)
  printf "\033[32;40;1m[+] $(date +'%Y-%m-%d %H:%M:%S') [WARN   ][$info_pattern] $(git_location_info)\033[0m: \033[43;30;1m$*\033[0m\n" 1>&2
  #func_log_print "warn" "[+] `date +'%H:%M:%S'` warn($info_pattern@$LINENO) `git_location_info`: $@" 1>&2;
}

echo_warn_detail() {
  local info_pattern=$(func_info_pattern)
  func_log_print "warn" "[+] $(date +'%Y-%m-%d %H:%M:%S') [WARN   ][$info_pattern] $(git_location_info): $*" 1>&2
}
#  ------------------------------------------------ 函数调用参数工具类  ---------------------------------------------------

_param_show_help(){
  if [[ $# -eq 0 ]] || [[ $1 == "-h"  ]] || [[ $1 == '--help'  ]] ; then
      echo yes
      return
  fi
  echo no
}


# 判定参数是否存在
# $1: 待检查的参数名
# $2: 参数列表
_param_exist(){
  local to_checked_param="$1"
  shift
  while [[ $# -gt 0 ]];
  do
    if [[ "$1" == "$to_checked_param" ]]; then
      echo "yes"
      return 0
    fi
    shift
  done
  echo "no"
  return 1
}

# 获取参数. 兼容短参数与长参数传递形式. 例如: --file="test.txt" 或者 -f "test.txt"
# 判定逻辑, 优先使用长参数, 如果长参数不存在, 则使用短参数. 如果短参数也不存在, 则返回空字符串
# 可以同时传入短参和长参. 短参和长参的优先级, 以传入的参数顺序为准, 参数名用 | 分隔, 例如: -f|--file
# 调用示例:
# param=$(_param_parse "--paramName" "$@")
# param=$(_param_parse "-p" "$@")
# param=$(_param_parse "-p|--paramName" "$@")
_param_parse() {
  # >&2 echo "param_parse: $*"
  local target_arg_name="$1"
  shift 1
  local target_arg_name1="${target_arg_name%%|*}" # 删除最长的 匹配 |* 的后缀子串,剩下的就是参数名1
  local target_arg_name2="${target_arg_name#*|}"  # 删除最短的 匹配 *| 的前缀子串, 剩下的就是参数名2
  local arg_name
  local arg_value=""

  # >&2 echo "param_parse: $*"
  local _param
  local _param_name
  local _param_value

  while [[ $# -gt 0 ]]; do
    _param="$1"
    _param_name="${_param%%=*}" # 删除最长的 匹配 =* 的后缀子串,剩下的就是参数名
    _param_value=""
    # 如果参数中包含 = , 则获取参数值
    if [[ "$_param" =~ .*=.* ]]; then
      _param_value="${_param#*=}" # 删除最短的 匹配 *= 的前缀子串, 剩下的就是参数值
    fi
    # echo "param_parse: _param_name: ${_param_name}, _param_value: '${_param_value}'"
    # 参数名相同,发现目标参数, 且参数值为空 , 则获取下一个参数作为参数值

    if [[ "$_param_name" != "$target_arg_name1" ]] && [[ "${_param_name}" != "${target_arg_name2}" ]]; then
      shift 1
      continue
    fi

    if [[ -z "${_param_value}" ]]; then
      arg_name="$_param_name"
      arg_value="$2"
      shift 2
    else
      arg_name="$_param_name"
      arg_value="$_param_value"
      shift 1
    fi
    break
  done

  if [[ -z "${arg_value}" ]]; then
    echo_debug "param '${target_arg_name}' not found"
  fi
  echo "${arg_value}"
}

#  ------------------------------------------------ 环境设置.方便全局控制  ------------------------------------------------
if [[ $(_param_exist '-x' "$@") == yes ]]; then
    export debug=1
fi

# 请转入 server_id
echo "请输入 server_id"
read  server_id
server_id=$(func_trim "$server_id")
if [[ -z "$server_id" ]]; then
    echo_warn "请输入有效的 server_id (这个 id 可以在你的 confluence 的管理页找到)"
    exit 1
fi

echo "请输入 plugin id ,一般在插件后台为: com.xx.xxx 形式"
read  plugin_id
plugin_id=$(func_trim "$plugin_id")
if [[ -z "$plugin_id" ]]; then
    echo_warn "请输入有效的 plugin_id"
    exit 1
fi

echo_debug "server_id:${server_id},plugin_id=${plugin_id}"
docker exec -it confluence-8.5.2 java -jar /opt/atlassian/confluence/atlassian-agent.jar -s "$server_id" -p "$plugin_id" -m test@test.com -n test -o test